package pers.cz.codegen.describe;

import pers.cz.jefconfig.log.LogUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * @program: jef-code-generator
 * @description: java代码描述
 * @author: Cheng Zhi
 * @create: 2022-08-07 16:04
 **/
public class JavaUnit extends AbstractJavaElement {

    protected List<JavaElement> methods = new LinkedList<>();
    protected List<JavaElement> innerClazzs = new LinkedList<>();
    protected List<JavaElement> fields = new LinkedList<>();
    protected List<JavaElement> enums = new LinkedList<>();
    protected String ipackage;
    protected Set<String> imports = new LinkedHashSet<String>();
    protected List<String> interfaces = new LinkedList<>();
    protected String parentName;
    protected boolean isChild = false;
    protected boolean isImpl = false;
    protected boolean isEnum = false;
    protected String classType = "class";

    public JavaUnit(String name) {
        setName(name);
    }

    public JavaUnit(String name, UnitType unitType) {
        setName(name);
        this.classType = unitType.getKey();
    }

    public void setIpackage(String ipackage) {
        this.ipackage = ipackage;
    }

    /**
     * 添加一个接口
     * @param interfaceName
     */
    public void addInterface(String interfaceName) {
        interfaces.add(interfaceName);
        this.isImpl = true;
    }

    /**
     * 添加一个接口
     * @param iClass
     */
    public void addInterface(IClass iClass) {
        this.addInterface(iClass.getName());
    }

    public void setParent(String iClassName) {
        this.parentName = iClassName;
        this.isChild = true;
    }

    /**
     * 添加一个父类
     * @param iClass
     */
    public void setParent(IClass iClass) {
        this.setParent(iClass.getName());
    }

    /**
     * 导入一个包
     * @param impt
     */
    public void addImport(String impt) {

        // java.lang不需要导包
        if (impt.startsWith("java.lang.")) {
            return;
        }
        this.imports.add(impt);
    }

    /**
     * 导入一个包
     * @param iClass
     */
    public void addImport(IClass iClass) {
        addImport(iClass.getName());
    }


    /**
     * 添加一个方法
     * @param javaElement
     */
    public void addMethod(JavaElement javaElement) {
        methods.add(javaElement);
    }

    /**
     * 添加一个全局变量
     * @param javaElement
     */
    public void addField(JavaElement javaElement) {
        fields.add(javaElement);
    }

    /**
     * 添加一个内部类
     * @param javaElement
     */
    public void addInnerClazz(JavaElement javaElement) {
        innerClazzs.add(javaElement);
    }

    /**
     * 添加一个枚举
     * @param javaElement
     */
    public void addEnum(JavaElement javaElement) {
        enums.add(javaElement);
        isEnum = true;
    }

    @Override
    public String toCode() {

        StringBuilder sb = new StringBuilder();
        sb.append("package ");
        sb.append(ipackage);
        sb.append(SEMICOLON);
        sb.append(LINE_SYMBOL);
        sb.append(LINE_SYMBOL);
        for (String impt : imports) {
            sb.append("import ");
            sb.append(impt);
            sb.append(SEMICOLON);
            sb.append(LINE_SYMBOL);
        }
        sb.append(LINE_SYMBOL);
        sb.append(getComment());
        sb.append(LINE_SYMBOL);

        for (String annotation : this.annotations) {
            sb.append(annotation);
            sb.append(LINE_SYMBOL);
        }
        sb.append(getModifer());
        sb.append(classType);
        sb.append(SPACE_SYMBOL);
        sb.append(getName());

        if (isChild) {
            sb.append(SPACE_SYMBOL);
            sb.append("extends");
            sb.append(SPACE_SYMBOL);
            sb.append(this.parentName);
        }

        if (isImpl) {
            sb.append(SPACE_SYMBOL);
            sb.append("implements");
            sb.append(SPACE_SYMBOL);
            int countInf = 0;
            int infSize = interfaces.size();
            for (String interfaceName : interfaces) {

                sb.append(interfaceName);
                countInf ++;
                if (countInf != infSize) {
                    sb.append(",");
                }
            }
        }

        sb.append(SPACE_SYMBOL);
        sb.append(START_SYMBOL);

        if (isEnum) {
            sb.append(LINE_SYMBOL);
            int enumConut = 0;
            for (JavaElement enumEnty : enums) {
                sb.append(enumEnty.toCode());
                enumConut ++;
                if (enumConut != enums.size()) {
                    sb.append(",");

                } else {
                    sb.append(SEMICOLON);
                }
                sb.append(LINE_SYMBOL);
            }
        }

        for (JavaElement filed : fields) {
            sb.append(filed.toCode());
        }

        for (JavaElement method : methods) {
            sb.append(method.toCode());
        }

        sb.append(LINE_SYMBOL);
        for (JavaElement innerClazz : innerClazzs) {
            sb.append(innerClazz);
        }

        sb.append(LINE_SYMBOL);
        sb.append(END_SYMBOL);

        return sb.toString();
    }

    /**
     * 将字符串写入到buffer
     * @param bw
     * @throws IOException
     */
    public void writeTo(BufferedWriter bw) throws IOException {

        // 调用tocode获取到字符串，将字符串写入到buffer
        bw.write(toCode());
        bw.flush();
    }

    /**
     * 获取一个字符缓冲
     * @param file
     * @param charset
     * @param append
     * @return
     */
    public BufferedWriter getWriter(File file, Charset charset, boolean append) throws FileNotFoundException {
        // 检查文件夹是否存在，
        ensureParentFolder(file);
        FileOutputStream fileOutputStream = new FileOutputStream(file, append);
        if (charset == null) {
            charset = Charset.defaultCharset();
        }
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, charset);
        return new BufferedWriter(outputStreamWriter);
    }

    /**
     * 检查/创建文件在所的文件夹。 如果该文件所在的文件夹已存在，什么也不做。 如果该文件所在的文件夹不存在，则创建
     *
     * @param file
     *            要检查的路径
     */
    public static void ensureParentFolder(File file) {
        File f = file.getParentFile();
        if (f != null && !f.exists()) {
            f.mkdirs();
        } else if (f != null && f.isFile()) {
            throw new RuntimeException(f.getAbsolutePath() + " is a exist file, can't create directory.");
        }
    }
    /**
     * 将生成的代码保存到指定文件夹下,最终按照包名称保存
     * 比如：srcFolder: /src/main/java  最终生成的代码将会在：/src/main/java/pers/cz/test/
     * @param srcFolder  /src/main/java
     * @param charset
     */
    public File saveToSrcFolder(File srcFolder, Charset charset) {

        String path = getPackagePath() + "/" + getName() + ".java";
        File file = new File(srcFolder.getAbsolutePath(), path);
        // 将file转换为BufferWrite
        try {
            BufferedWriter writer = getWriter(file, charset, false);
            writeTo(writer);
            writer.flush();
            writer.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return file;
    }

    /**
     * 将包名，转化为路径
     * @return
     */
    public String getPackagePath() {

        return this.ipackage.replace(".", "/");
    }

    @Override
    public String toString() {
        StringWriter sw=new StringWriter();
        BufferedWriter bw=new BufferedWriter(sw);
        try {
            this.writeTo(bw);
        } catch (IOException e) {
            LogUtils.error("异常" + e.getMessage());
        }
        return sw.toString();
    }

    public enum UnitType {
        CLASS("class"),
        ENUM("enum"),
        INTERFACE("interface"),
        ABSTRACT_CLASS("abstract class");

        private String key;

        UnitType(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }
    }
}
